package dialect

import (
	"database/sql"
	"fmt"
	"strings"

	"code_gen/config"
	"code_gen/db"
	"code_gen/entity"
	"code_gen/logger"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func init() {
	RegisterDialect(DialectSqlite, &SqliteDialect{})
	RegisterDialectDataTypeMapping(DialectSqlite, GetJavaCommonDataTypeMapping, nil)
	RegisterDialectDataTypeMapping(DialectSqlite, GetGolangCommonDataTypeMapping, nil)
}

type SqliteDialect struct {
}

func (c *SqliteDialect) ConvertDataType(ctx *Context, langType, dataType string) string {
	return GetLangDataType(DialectSqlite, langType, dataType)
}
func (c *SqliteDialect) GetDB(ctx *Context, ds *config.DataSource) (*gorm.DB, error) {
	if ds.Type == "sql" {
		return nil, nil
	}

	dsn := ds.DataBase
	return db.CreateDB(sqlite.Open(dsn))
}
func (c *SqliteDialect) GetTableInfo(ctx *Context, ds *config.DataSource, db *gorm.DB, table string) (*entity.TableInfo, error) {
	if ds.Type == "db" {
		return c.getTableInfoFromDb(ctx, ds, db, table)
	} else {
		return c.getTableInfoFromSql(ctx, ds, table)
	}
}
func (c *SqliteDialect) getTableInfoFromDb(ctx *Context, ds *config.DataSource, db *gorm.DB, tableName string) (*entity.TableInfo, error) {

	// 获取表字段信息
	var rows []map[string]interface{}
	//goland:noinspection ALL
	sql := fmt.Sprintf("PRAGMA table_info(%s);", tableName)
	//sql := fmt.Sprintf("select * from %s", tableName)
	logger.Log.Info("sqlite get column info sql: ", sql)

	rs, e := db.Raw(sql).Rows()
	if e != nil {
		return nil, e
	}
	cols, e := rs.Columns()
	if e != nil {
		return nil, e
	}
	for rs.Next() {
		row, err := scanMap(rs, cols)
		if err != nil {
			return nil, err
		}
		rows = append(rows, row)
	}

	if len(rows) < 1 {
		return nil, fmt.Errorf("table %s column is illegally", tableName)
	}

	var columns []*entity.ColumnInfo
	for _, row := range rows {
		columns = append(columns, c.convertColumn(ctx, row, ds.Lang))
	}

	tableInfo := &entity.TableInfo{}
	tableInfo.Dialect = DialectSqlite
	tableInfo.Lang = ds.Lang
	tableInfo.Name = tableName
	tableInfo.Comment = ""
	tableInfo.Columns = columns

	// 导入的包名 去重
	uniqueMap := make(map[string]bool)
	for _, column := range columns {
		dtm := GetDataTypeMapping(DialectSqlite, ds.Lang, column.DataType)
		if dtm != nil && dtm.LangPackage != "" {
			uniqueMap[dtm.LangPackage] = true
		}
	}
	uniqueArray := make([]string, 0, len(uniqueMap))
	for str := range uniqueMap {
		uniqueArray = append(uniqueArray, str)
	}

	tableInfo.Packages = uniqueArray

	return tableInfo, nil
}
func (c *SqliteDialect) convertColumn(ctx *Context, dbColumn map[string]interface{}, lang string) *entity.ColumnInfo {

	ci := &entity.ColumnInfo{}
	ci.Name = dbColumn["name"].(string)
	ci.Comment = "" // sqlite 获取不到注释
	ci.DataType = dbColumn["type"].(string)
	ci.DataType = strings.ToLower(ci.DataType) // 转成小写
	ci.PrimaryKeyFlag = fmt.Sprint(dbColumn["pk"].(int))
	ci.IsPrimaryKey = ci.PrimaryKeyFlag == "1"
	ci.LangDataType = c.ConvertDataType(ctx, lang, ci.DataType)

	ci.Raw = dbColumn

	return ci

}
func (c *SqliteDialect) getTableInfoFromSql(ctx *Context, ds *config.DataSource, sql string) (*entity.TableInfo, error) {
	return nil, nil
}

// PRAGMA table_info(test) 由于gorm不能直接扫描出来值，只能用此函数完成
func scanMap(rs *sql.Rows, columns []string) (map[string]interface{}, error) {

	var m = make(map[string]interface{}, 6)

	var cid, notnull, pk int
	var name, typ string
	var dfltValue interface{}

	e := rs.Scan(&cid, &name, &typ, &notnull, &dfltValue, &pk)
	if e != nil {
		return nil, e
	}

	m[columns[0]] = cid
	m[columns[1]] = name
	m[columns[2]] = typ
	m[columns[3]] = notnull
	m[columns[4]] = dfltValue
	m[columns[5]] = pk

	return m, nil
}
